package per.turtle.job.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import org.springframework.cloud.openfeign.FeignClient;
import per.turtle.common.core.annotation.CustomUseForJob;
import per.turtle.job.domain.SysJob;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

/**
 * 任务执行工具
 *
 * @author turtle
 */
public class JobInvokeUtil {

    /**
     * 获取 method 方法参数相关列表
     *
     * @param paramsStr 参数字符串
     * @return method方法相关参数列表
     */
    private static List<Object> getMethodParams(String paramsStr) {
        List<Object> objList = new LinkedList<>();
        if (StrUtil.isBlank(paramsStr)) {
            return objList;
        }
        // 逗号分割
        String[] methodParams = paramsStr.split(StrUtil.COMMA + "(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
        for (String methodParam : methodParams) {
            String str = StrUtil.trimToEmpty(methodParam);
            // String字符串类型，包含 ' 或 "开头
            if (StrUtil.startWithAny(str, "'", "\"") && StrUtil.endWithAny(str, "'", "\"")) {
                objList.add(StrUtil.sub(str, 1, str.length() - 1));
            }
            // boolean布尔类型，等于true或者false
            else if (StrUtil.equals(str, "true") || StrUtil.equalsIgnoreCase(str, "false")) {
                objList.add(Boolean.valueOf(str));
            }
            // long长整形，包含L
            else if (StrUtil.containsIgnoreCase(str, "L")) {
                objList.add(Long.valueOf(StrUtil.replaceIgnoreCase(str, "L", "")));
            }
            // double浮点类型，包含D
            else if (StrUtil.containsIgnoreCase(str, "D")) {
                objList.add(Double.valueOf(StrUtil.replaceIgnoreCase(str, "D", "")));
            }
            // 其他类型归类为整形
            else {
                objList.add(Integer.valueOf(str));
            }
        }
        return objList;
    }

    /**
     * 获取参数类型
     *
     * @param methodParams 参数相关列表
     * @return 参数类型列表
     */
    public static Class<?>[] getMethodParamsType(List<Param> methodParams) {
        Class<?>[] clzs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Param p : methodParams) {
            clzs[index] = p.getType();
            index++;
        }
        return clzs;
    }

    /**
     * 获取参数值
     *
     * @param methodParams 参数相关列表
     * @return 参数值列表
     */
    public static Object[] getMethodParamsValue(List<Param> methodParams) {
        Object[] objs = new Object[methodParams.size()];
        int index = 0;
        for (Param p : methodParams) {
            objs[index] = p.getValue();
            index++;
        }
        return objs;
    }

    /**
     * 执行方法
     *
     * @param sysJob 系统任务
     */
    public static void invokeMethod(SysJob sysJob) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException,
                                                          NoSuchMethodException {
        String className = sysJob.getInvokeClass();
        String methodName = sysJob.getInvokeMethod();
        List<Param> methodParams = JSONUtil.toList(sysJob.getInvokeParams(), Param.class);

        Object bean = SpringUtil.getBean(Class.forName(className));
        if (BeanUtil.isEmpty(bean)) {
            bean = Class.forName(className).newInstance();
        }
        // todo 通过 FeignClient 跨服务调用时
//        if (bean.getClass().isAnnotationPresent(CustomUseForJob.class)) {
//            bean.getClass().geta
//        }
        invokeMethod(bean, methodName, methodParams);

    }

    /**
     * 调用任务方法
     *
     * @param bean         目标对象
     * @param methodName   方法名称
     * @param methodParams 方法参数
     */
    private static void invokeMethod(Object bean, String methodName, List<Param> methodParams) throws NoSuchMethodException, SecurityException,
                                                                                                      IllegalAccessException, IllegalArgumentException,
                                                                                                      InvocationTargetException {
        if (BeanUtil.isNotEmpty(methodParams) && methodParams.size() > 0) {
            Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
            method.invoke(bean, getMethodParamsValue(methodParams));
        } else {
            Method method = bean.getClass().getDeclaredMethod(methodName);
            method.invoke(bean);
        }
    }

    @Data
    private static class Param {

        Class<?> type;

        Object value;

    }

}
